package com.wuzheng.common.obs.upload.service;

import com.obs.services.ObsClient;
import com.obs.services.ObsConfiguration;
import com.obs.services.exception.ObsException;
import com.obs.services.model.*;
import com.wuzheng.common.obs.upload.annotation.ObsClientAnnotation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.List;
import java.util.UUID;

/**
 * Title: ObsUtils
 * Description: obs操作工具类
 *
 * @author hfl
 * @version V1.0
 * @date 2020-05-27
 */
public class ObsService {
    private final static Logger logger = LoggerFactory.getLogger(ObsService.class);

    private ObsClient obsClient;
    private String bucketName;
    private String bucketLoc;
    private ObsConfiguration config;

    public ObsService() {
    }

    public ObsService(String bucketName, String bucketLoc, ObsConfiguration config) {
        this.bucketName = bucketName;
        this.bucketLoc = bucketLoc;
        this.config = config;
    }

    /**
     * 查询所有桶
     *
     * @return 桶的列表
     */
    @ObsClientAnnotation
    public List<ObsBucket> listBuckets() {
        ListBucketsRequest request = new ListBucketsRequest();
        request.setQueryLocation(true);
        List<ObsBucket> obsBuckets = obsClient.listBuckets(request);
        return obsBuckets;
    }

    /**
     * 桶的新增
     *
     * @param bucketName 桶的名称
     * @return 桶对象信息
     */
    @ObsClientAnnotation
    public ObsBucket createBucket(String bucketName) {
        ObsBucket obsBucket = new ObsBucket(bucketName, bucketLoc);
        obsBucket.setBucketName(bucketName);
        return obsClient.createBucket(obsBucket);
    }

    /**
     * 桶的删除
     *
     * @param bucketName 桶的名称
     * @return 响应对象
     */
    @ObsClientAnnotation
    public HeaderResponse deleteBucket(String bucketName) {
        return obsClient.deleteBucket(bucketName);
    }

    /**
     * 设置桶内指定前缀的文件对象 过期时间
     *
     * @param prefix         什么前缀的文件过期（比如/tmp）
     * @param expirationDays 几天后过期
     */
    @ObsClientAnnotation
    public HeaderResponse setLiftConfig(String prefix, Integer expirationDays) {
        LifecycleConfiguration config = new LifecycleConfiguration();
        LifecycleConfiguration.Rule rule = config.new Rule();
        rule.setEnabled(true);
        //过期规则名称
        rule.setId(String.valueOf(UUID.randomUUID()));
        rule.setPrefix(prefix);
        LifecycleConfiguration.Expiration expiration = config.new Expiration();
        // 指定满足前缀的对象创建x天后过期
        expiration.setDays(expirationDays);
        LifecycleConfiguration.NoncurrentVersionExpiration noncurrentVersionExpiration = config.new NoncurrentVersionExpiration();
        // 指定满足前缀的对象成为历史版本x天后过期
        noncurrentVersionExpiration.setDays(expirationDays);
        rule.setNoncurrentVersionExpiration(noncurrentVersionExpiration);
        config.addRule(rule);
        return obsClient.setBucketLifecycle(bucketName, config);
    }

    /**
     * 查询桶的所有对象
     *
     * @return 桶内对象集合（关键属性getObjectKey）
     */
    @ObsClientAnnotation
    public ObjectListing listObjects() {
        ObjectListing listing = obsClient.listObjects(bucketName);
        return listing;
    }

    /**
     * 上传对象时指定预定义访问策略为公共读(很重要)
     *
     * @param objectKey   对象的key
     * @param inputStream 要上传的文件流
     * @return 响应对象
     */
    @ObsClientAnnotation
    public PutObjectResult putObjectAndSetPreAccessStrategy(String objectKey, InputStream inputStream) {
        PutObjectRequest request = new PutObjectRequest();
        request.setBucketName(bucketName);
        //对象的key 如:  objectname1/text
        request.setObjectKey(objectKey);
        request.setInput(inputStream);
        //设置对象访问权限为公共读
        request.setAcl(AccessControlList.REST_CANNED_PUBLIC_READ);
        return obsClient.putObject(request);
    }

    /**
     * 上传对象时指定预定义访问策略为公共读(很重要)
     *
     * @param objectKey   对象的key
     * @param file 要上传的文件
     * @return 响应对象
     */
    @ObsClientAnnotation
    public PutObjectResult putObjectAndSetPreAccessStrategy(String objectKey, File file) {
        PutObjectRequest request = new PutObjectRequest();
        request.setBucketName(bucketName);
        //对象的key 如:  objectname1/text
        request.setObjectKey(objectKey);
        request.setFile(file);
        //设置对象访问权限为公共读
        request.setAcl(AccessControlList.REST_CANNED_PUBLIC_READ);
        return obsClient.putObject(request);
    }

    /**
     * 上传某个对象并设置对象自定义元数据
     *
     * @param objectKey   要上传的key
     * @param metadata    元数据对象
     * @param inputStream 要上传的文件流
     * @return 上传结果
     * //  ObjectMetadata metadata = new ObjectMetadata();
     * //  metadata.addUserMetadata("property1", "property-value1");
     * //  metadata.getMetadata().put("property2", "property-value2");
     * //  C:\\Users\\hufanglei\\Pictures\\timg.jpg"
     */
    @ObsClientAnnotation
    public PutObjectResult putObjectAndSetMeta(String objectKey, ObjectMetadata metadata, InputStream inputStream) {
        return obsClient.putObject(bucketName, objectKey, inputStream, metadata);
    }

    /**
     * 获取某个对象
     *
     * @param objectKey 对象的key
     * @return 对象的信息
     */
    @ObsClientAnnotation
    public ObsObject getObject(String objectKey) {
        return obsClient.getObject(bucketName, objectKey, null);
    }

    /**
     * 获取某个对象的流
     *
     * @param objectKey 对象的key
     * @return 对象的流
     */
    @ObsClientAnnotation
    public InputStream getObjectInpuStream(String objectKey) {
        ObsObject obsObject = obsClient.getObject(bucketName, objectKey, null);
        return obsObject.getObjectContent();
    }

    /**
     * 查询对象元数据(查询某个对象的具体信息)
     *
     * @param objectKey 要查询的key
     * @return 对象元数据
     */
    @ObsClientAnnotation
    public ObjectMetadata getObjectMetadata(String objectKey) {
        //获取对象元数据
        return obsClient.getObjectMetadata(bucketName, objectKey, null);
    }

    /**
     * 拷贝对象(也可以从一个桶拷贝到另一个桶，这里目前桶和原始文件桶都设置成了配置文件中的桶)
     *
     * @param sourceObjectKey 原始key
     * @param destObjectKey   目标key
     * @return 响应结果
     */
    @ObsClientAnnotation
    public CopyObjectResult copyObject(String sourceObjectKey, String destObjectKey) {
        String sourceBucketName = bucketName;
        String destBucketName = bucketName;
        return obsClient.copyObject(sourceBucketName, sourceObjectKey, destBucketName, destObjectKey);
    }

    /**
     * 删除单个对象
     *
     * @param objectKey 要删除的key
     * @return 响应结果
     */
    @ObsClientAnnotation
    public DeleteObjectResult deletObj(String objectKey) {
        return obsClient.deleteObject(bucketName, objectKey);
    }
    /**
     * 下载某个对象到本地
     *
     * @param objectKey     对象的key
     * @param localFilePath 本地文件路径
     * @throws ObsException
     * @throws IOException
     */
    @ObsClientAnnotation
    public void downloadToLocalFile(String objectKey, String localFilePath) throws ObsException, IOException {
        File localFile = new File(localFilePath);
        if (!localFile.getParentFile().exists()) {
            localFile.getParentFile().mkdirs();
        }
        ObsObject obsObject = obsClient.getObject(bucketName, objectKey, null);
        ReadableByteChannel rchannel = Channels.newChannel(obsObject.getObjectContent());
        ByteBuffer buffer = ByteBuffer.allocate(4096);
        WritableByteChannel wchannel = Channels.newChannel(new FileOutputStream(new File(localFilePath)));
        while (rchannel.read(buffer) != -1) {
            buffer.flip();
            wchannel.write(buffer);
            buffer.clear();
        }
        rchannel.close();
        wchannel.close();
    }

    /**
     * 创建一个文件夹，必须带有/后缀
     *
     * @param keySuffixWithSlash1 文件夹名称（必须带有/后缀）
     * @return 响应对象
     */
    @ObsClientAnnotation
    public PutObjectResult createEmptyFolder(String keySuffixWithSlash1) {
        return obsClient.putObject(bucketName, keySuffixWithSlash1, new ByteArrayInputStream(new byte[0]));
    }


    /**
     * 判断对象是否是文件夹
     * @param keySuffixWithSlash1 文件夹名: 如:   "MyObjectKey1/"
     * @return 布尔值
     */
    @ObsClientAnnotation
    public boolean isEmptyFolder(String keySuffixWithSlash1)  {
        ObsObject object = obsClient.getObject(bucketName, keySuffixWithSlash1, null);
        if (object != null) {
            return object.getMetadata().getContentLength() == 0L;
        }
        return false;
    }

    /**
     * 列举某个文件夹下的所有对象
     *
     * @param folderPrefix 件夹名（必须/结尾）
     * @return 对象集合
     */
    @ObsClientAnnotation
    public ObjectListing listObjectsByFolder(String folderPrefix) {
        ListObjectsRequest request = new ListObjectsRequest(bucketName);
        // 设置文件夹对象名"dir/"为前缀
        // request.setPrefix("dir/");
        request.setPrefix(folderPrefix);
        //列举文件个数
        request.setMaxKeys(1000);
        ObjectListing result;
        do {
            result = obsClient.listObjects(request);
            for (ObsObject obsObject : result.getObjects()) {
                logger.info("\t" + obsObject.getObjectKey());
                logger.info("\t" + obsObject.getOwner());
            }
            request.setMarker(result.getNextMarker());
        } while (result.isTruncated());
        return result;
    }

    /**
     * 删除某个文件夹下的所有对象
     *
     * @param folderPrefix 文件夹名（必须/结尾）
     * @return 是否删除成功
     */
    @ObsClientAnnotation
    public boolean deleteListObjectsByFolder(String folderPrefix) {
        ListObjectsRequest request = new ListObjectsRequest(bucketName);
        // 设置文件夹对象名"/xx/xxx/"为前缀
        request.setPrefix(folderPrefix);
        //列举文件个数
        request.setMaxKeys(1000);
        ObjectListing result;
        do {
            result = obsClient.listObjects(request);
            for (ObsObject obsObject : result.getObjects()) {
                logger.info("\t" + obsObject.getObjectKey());
                logger.info("\t" + obsObject.getOwner());
                //执行删除
                obsClient.deleteObject(bucketName, obsObject.getObjectKey());
            }
            request.setMarker(result.getNextMarker());
        } while (result.isTruncated());
        return true;
    }


    public ObsClient getObsClient() {
        return obsClient;
    }

    public void setObsClient(ObsClient obsClient) {
        this.obsClient = obsClient;
    }

    public String getBucketName() {
        return bucketName;
    }

    public void setBucketName(String bucketName) {
        this.bucketName = bucketName;
    }

    public String getBucketLoc() {
        return bucketLoc;
    }

    public void setBucketLoc(String bucketLoc) {
        this.bucketLoc = bucketLoc;
    }

    public ObsConfiguration getConfig() {
        return config;
    }

    public void setConfig(ObsConfiguration config) {
        this.config = config;
    }
}
